package com.ezlcp.user.web;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.ezlcp.commons.base.db.BaseService;
import com.ezlcp.commons.base.entity.BaseEntity;
import com.ezlcp.commons.base.entity.JsonPageResult;
import com.ezlcp.commons.base.entity.JsonResult;
import com.ezlcp.commons.base.entity.QueryData;
import com.ezlcp.commons.base.search.QueryFilter;
import com.ezlcp.commons.base.search.QueryFilterBuilder;
import com.ezlcp.commons.tool.BeanUtil;
import com.ezlcp.commons.tool.StringUtils;
import com.ezlcp.commons.utils.ContextUtil;
import com.ezlcp.commons.utils.ExceptionUtil;
import com.ezlcp.commons.utils.FileUtil;
import com.ezlcp.user.service.LogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 包名：com.ezlcp.commons.web.controller
 * 功能描述：提供控制器一些基础方法，如获得i18n国际化的方法等
 */
@RestController
public abstract class BaseController<E extends BaseEntity<? extends Serializable>> {
    @Resource
    public LogService logService;
    protected Logger logger = LoggerFactory.getLogger(BaseController.class);
    @Autowired
    protected HttpServletRequest request;
    
    public abstract BaseService<E> getBaseService();
    
    /**
     * 导出接口。
     * @return
     */
    protected IExport getExport() {
        return null;
    }

    /**
     * 获取注释。
     *
     * @return
     */
    public abstract String getComment();

    /**
     * 子类可以添加对这个过滤器添加条件。
     *
     * @param filter
     */
    protected void handleFilter(QueryFilter filter) {
        String tenantId = ContextUtil.getCurrentTenantId();
        if (StringUtils.isNotEmpty(tenantId)) {
            filter.addQueryParam("Q_tenant_id_S_EQ", tenantId);
        }
    }

    /**
     * 返回当前上下文的租户ID
     *
     * @return
     */
    protected String getCurrentTenantId() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        var request = attributes.getRequest();
        String tenantId = request.getParameter("tenantId");
        if (StringUtils.isEmpty(tenantId)) {
            tenantId = ContextUtil.getCurrentTenantId();
        }
        return tenantId;
    }

    /**
     * 子类可以增加这个过滤器处理数据
     *
     * @param page
     */
    protected void handlePage(IPage page) {

    }

    /**
     * 查询列表时，可以复写这个方法对列表进行更改。
     *
     * @param list
     */
    protected void handleList(List list) {

    }

    /**
     * 子类可以增加这个过滤器处理数据
     *
     * @param ent
     */
    protected void handleData(E ent) {

    }

    protected JsonResult beforeSave(E ent) {
        return JsonResult.Success();
    }

    protected JsonResult beforeRemove(List<String> list) {
        return JsonResult.Success();
    }

    @Operation(summary = "根据主键ID删除记录", description = "根据主键ID删除记录,parameters is {ids:'1,2'}")
    @PostMapping("del")
    public JsonResult del(@RequestParam(value = "ids") String ids) {

        if (StringUtils.isEmpty(ids)) {
            return new JsonResult(false, "common.paramError");
        }
        String[] aryId = ids.split(",");
        List list = Arrays.asList(aryId);

        JsonResult rtn = beforeRemove(list);
        if (!rtn.isSuccess()) {
            return rtn;
        }
        JsonResult result = JsonResult.getSuccessResult("common.delSuccess");
        for (Object id : list) {
            String idStr = id.toString();
            E ent = getBaseService().get(idStr);
            if (ent == null) {
                continue;
            }
            //记录日志
            logService.saveSystemLog(getComment(),"delete",idStr, JSON.toJSONString(ent));
        }

        getBaseService().delete(list);
        return result;
    }

    @Operation(summary = "查询当前业务表的所有数据", description = "查询当前实体表的所有数据。")
    @PostMapping("getAll")
    public JsonPageResult getAll() throws Exception {
        List data = getBaseService().getAll();
        JsonPageResult result = new JsonPageResult();
        result.setData(data);
        result.setShow(false);
        //记录日志
        logService.saveSystemLog(getComment(),"getAll",null, "");
        return result;
    }

    @Operation(summary = "保存业务数据记录", description = "根据提交的业务JSON数据保存业务数据记录")
    @Parameter(name = "entity", description = "实体对象")
    @PostMapping("/save")
    public JsonResult save(@Validated @RequestBody E entity, BindingResult validResult) throws Exception {
        JsonResult result = handValid(validResult);
        if (!result.isSuccess()) {
            return result;
        }

        Serializable pkId = entity.getPkId();
        String str = "";
        String operation = "";
        try {
            result = beforeSave(entity);
            if (!result.isSuccess()) {
                return result;
            }
            if (BeanUtil.isEmpty(pkId)) {
                getBaseService().insert(entity);

                str = "common.addSuccess";
                //记录日志
                logService.saveSystemLog(getComment(),"add",entity.getPkId().toString(), JSON.toJSONString(entity));
            } else {
                E oldEnt = getBaseService().get(pkId);
                getBaseService().update(entity);
                str = "user.updateSuccess";
                //记录日志
                String detail = "old:" + JSON.toJSONString(oldEnt) + ",new:" + JSON.toJSONString(entity);
                logService.saveSystemLog(getComment(),"update",entity.getPkId().toString(), detail);
            }
            result = JsonResult.getSuccessResult(str);
            result.setData(entity);
        } catch (Exception ex) {
            String errMsg = ExceptionUtil.getExceptionMessage(ex);

            if (BeanUtil.isEmpty(pkId)) {
                str = "common.addFail";
            } else {
                str = "user.updateFail";
            }
            result = JsonResult.getFailResult(str);
            result.setData(errMsg);
        }
        return result;
    }

    /**
     * 检查数据是否合法
     *
     * @param validResult
     * @return
     */
    public JsonResult handValid(BindingResult validResult) {
        if (!validResult.hasErrors()) {
            return JsonResult.Success();
        }
        JsonResult result = JsonResult.Fail("common.formValidationFail");
        List<ObjectError> allErrors = validResult.getAllErrors();
        StringBuilder sb = new StringBuilder();
        for (ObjectError error : allErrors) {
            if (error instanceof FieldError) {
                sb.append("[" + ((FieldError) error).getField() + "]");
            }
            sb.append(error.getDefaultMessage());
            sb.append("\r\n");
        }
        result.setData(sb.toString());

        return result;

    }

    /**
     * 获得所有查询数据列表，不传入条件时，则返回所有的记录
     *
     * @return
     * @throws Exception
     */
    @Operation(summary = "★.多条件分页查询",
            description = "<b>查询参数params详细说明:</b>" +
                    "<br>每个参数名格式为：Q_数据库字段名_字段类型缩写_操作符缩写，参数值为查询条件值。" +
                    "<br>数据库字段名，如果是多表关联查询，且有字段名相同，就要加上“表名.”作为前缀，或者“表别名.”" +
                    "<br>字段类型缩写：S:字符串，L:长整型，I:整形，DB:DOUBLE，BD:BIGDECIMAL，F:FLOAT，SN:SHORT，D:日期类型，" +
                    "<br>操作符缩写：EQ:=，NEQ:!=，GT:>，GE:>=，LT:<，LE:<=，LK:全匹配，RIK:右匹配，LEK:左匹配，ISNULL:空，NOTNULL:非空" +
                    "<br>例如：{ \"Q_name_S_LK\": \"王\",  \"Q_user_type_S_EQ\": \"staff\"}，表示查询name字段中包含“王”字，且user_type字段值等于\"staff\"的记录;" +
                    "<br>前端查询输入框控件可绑定到类似“params.Q_name_S_LK”的变量，然后将params对象整体组装成JSON格式传到后台即可。")
    @PostMapping(value = "/query")
    public JsonPageResult query(@RequestBody QueryData queryData) throws Exception {
        JsonPageResult jsonResult = JsonPageResult.getSuccess("common.returnDataSuccess");
        try {
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            var request = servletRequestAttributes.getRequest();
            String url = request.getRequestURI();

            long start = System.currentTimeMillis();
            QueryFilter filter = QueryFilterBuilder.createQueryFilter(queryData);
            handleFilter(filter);
            IPage page = getBaseService().query(filter, queryData.getKey());
            handlePage(page);
            jsonResult.setPageData(page);
            //记录日志
            logService.saveSystemLog(getComment(),"query",null, JSON.toJSONString(queryData));
            logger.info("url:" + url + ",escape time:" + (System.currentTimeMillis() - start) + "ms");
        } catch (Exception ex) {
            jsonResult.setSuccess(false);
            logger.error(ExceptionUtil.getExceptionMessage(ex));
            jsonResult.setMessage(ExceptionUtil.getExceptionMessage(ex));
        }
        return jsonResult;
    }

    /**
     * 根据主键查询记录详细信息
     *
     * @param pkId
     * @return
     */
    @Operation(summary = "根据主键查询业务数据详细信息", description = "根据主键查询业务数据详细信息")
    @PostMapping("/get")
    public JsonResult<E> get(@RequestParam(value = "pkId") String pkId) {
        JsonResult result = JsonResult.Success();
        result.setShow(false);
        if (ObjectUtils.isEmpty(pkId)) {
            return result.setData(new Object());
        }
        E ent = getBaseService().get(pkId);
        handleData(ent);
        return result.setData(ent);
    }

    @Operation(summary = "根据主键IDS查询业务数据详细信息", description = "根据主键IDS查询业务数据详细信息")
    @PostMapping("/getByIds")
    public JsonResult<E> getByIds(@RequestParam(value = "ids") String ids) {
        JsonResult result = JsonResult.Success();
        result.setShow(false);
        if (ObjectUtils.isEmpty(ids)) {
            return result.setData(new Object());
        }
        String[] aryId = ids.split(",");
        List<String> idList = Arrays.asList(aryId);
        List<E> byIds = getBaseService().getByIds(idList);
        return result.setData(byIds);
    }


    //@ApiOperation("查询结果导出")
    //@PostMapping("/listExport")
    public void listExport(@RequestBody QueryData param) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        var response = attributes.getResponse();
        try {
            String[] all = null;
            if (param.getParams().containsKey("isQuery")) {
                QueryFilter filter = QueryFilterBuilder.createQueryFilter(param);
                List<? extends BaseEntity> lists = getBaseService().queryList(filter);
                if (lists == null) {
                    throw new Exception("导出失败，没有查询结果。");
                }
                List<String> ids = new ArrayList<>();
                for (BaseEntity list : lists) {
                    ids.add((String) list.getPkId());
                }
                all = ids.toArray(new String[ids.size()]);
            } else {
                String ids = param.getParams().get("ids");
                if (StringUtils.isEmpty(ids)) {
                    throw new RuntimeException("导出失败，请选择要导出的记录。");
                }
                all = ids.split(",");
            }
            IExport export = getExport();

            StringBuilder sb = new StringBuilder();
            sb.append("导出 " + getComment() + " :");


            Map<String, String> map = new HashMap<>();
            for (String id : all) {

                if (export == null) {
                    logger.debug("请实现IExport接口!");
                    continue;
                }
                JSONObject json = getExport().doExportById(id, sb);
                String fileName = id + ".json";
                String defStr = JSONObject.toJSONString(json);
                map.put(fileName, defStr);
            }


            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            String downFileName = getComment() + "_" + sdf.format(new Date());
            FileUtil.downloadZip(response, downFileName, map);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}
